package com.ruoyi.web.controller.cluster;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.agent.domain.KwMachineAgent;
import com.ruoyi.agent.service.IKwMachineAgentService;
import com.ruoyi.cluster.domain.KwClusterInfo;
import com.ruoyi.cluster.domain.KwClusterNodeInfo;
import com.ruoyi.cluster.domain.KwHugeClusterInfo;
import com.ruoyi.cluster.service.IKwClusterInfoService;
import com.ruoyi.cluster.service.IKwClusterNodeInfoService;
import com.ruoyi.cluster.service.IKwHugeClusterInfoService;
import com.ruoyi.cluster.vo.*;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.ClusterConstant;
import com.ruoyi.common.constant.ScheduleConstants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.exception.job.TaskException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.agent.AgentRequest;
import com.ruoyi.common.utils.agent.AgentRestUtil;
import com.ruoyi.common.utils.agent.AgentResult;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.machine.domain.KwMachineInfo;
import com.ruoyi.machine.service.IKwMachineInfoService;
import com.ruoyi.quartz.domain.SysJob;
import com.ruoyi.quartz.service.ISysJobService;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 基础信息——大集群列Controller
 * 
 * @author gonianer
 * @date 2023-11-30
 */
@RestController
@RequestMapping("/cluster/cluster")
public class KwHugeClusterInfoController extends BaseController
{
    @Autowired
    private IKwHugeClusterInfoService clusterService;
    @Autowired
    private IKwMachineAgentService agentService;
    @Autowired
    private IKwMachineInfoService machineService;
    @Autowired
    private IKwClusterInfoService subClsService;
    @Autowired
    private IKwClusterNodeInfoService nodeService;
    @Autowired
    private ISysJobService jobService;

    /**
     * 查询基础信息——大集群列列表
     */
    @PreAuthorize("@ss.hasPermi('cluster:cluster:list')")
    @GetMapping("/list")
    public TableDataInfo list(KwHugeClusterInfo kwHugeClusterInfo)
    {
        startPage();
        List<KwHugeClusterInfo> list = clusterService.selectKwHugeClusterInfoList(kwHugeClusterInfo);
        return getDataTable(list);
    }

    /**
     * 导出基础信息——大集群列列表
     */
    @PreAuthorize("@ss.hasPermi('cluster:cluster:export')")
    @Log(title = "基础信息——大集群列", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(HttpServletResponse response, KwHugeClusterInfo kwHugeClusterInfo)
    {
        List<KwHugeClusterInfo> list = clusterService.selectKwHugeClusterInfoList(kwHugeClusterInfo);
        ExcelUtil<KwHugeClusterInfo> util = new ExcelUtil<KwHugeClusterInfo>(KwHugeClusterInfo.class);
        util.exportExcel(response, list, "基础信息——大集群列数据");
    }

    /**
     * 获取基础信息——大集群列详细信息
     */
    @PreAuthorize("@ss.hasPermi('cluster:cluster:query')")
    @GetMapping(value = "/{id}")
    public AjaxResult getInfo(@PathVariable("id") Long id)
    {
        return success(clusterService.selectKwHugeClusterInfoById(id));
    }

    /**
     * 新增基础信息——大集群列
     */
    @PreAuthorize("@ss.hasPermi('cluster:cluster:add')")
    @Log(title = "基础信息——大集群列", businessType = BusinessType.INSERT)
    @PostMapping
    public AjaxResult add(@RequestBody KwHugeClusterInfo kwHugeClusterInfo)
    {
        return toAjax(clusterService.insertKwHugeClusterInfo(kwHugeClusterInfo));
    }

    /**
     * 创建大集群——检查
     */
    @PreAuthorize("@ss.hasPermi('cluster:cluster:add')")
    @Log(title = "创建大集群——检查", businessType = BusinessType.OTHER)
    @PostMapping("/precheck")
    public AjaxResult precheck(@RequestBody ClusterData cluster) throws SchedulerException, TaskException {
        List<CheckResult> checkResult = new ArrayList();
        ClusterBasic basic = cluster.getCluster();
        List<ClusterZone> zones = cluster.getZones();
        //检查集群名称是否被占用
        KwHugeClusterInfo query = new KwHugeClusterInfo();
        query.setHclusterId(basic.getHclusterId());
        List<KwHugeClusterInfo> exists = clusterService.selectKwHugeClusterInfoList(query);
        if(!CollectionUtils.isEmpty(exists)){
            checkResult.add(new CheckResult("集群编号："+basic.getHclusterId()+"已存在"));
        }

        zones.stream().forEach(zone ->{
            zone.getNodes().stream().forEach(node -> {
                KwMachineInfo machine = machineService.selectKwMachineInfoByIp(node.getIp());
                KwMachineAgent agent = agentService.selectKwMachineAgent(machine.getId());
                //检查节点端口是否被占用
                AgentRequest request = new AgentRequest(node.getIp(),agent.getPort(),agent.getHttpUser(),agent.getHttpPasswd());
                if(node.getMg()==1){
                    request.setCommand(AgentRestUtil.COMMAND_CHECK_PORT_USED+basic.getMgPort());
                    AgentResult result = AgentRestUtil.postAgentCommand(request);
                    if(result.getData().getRetcode() == 0){
                        checkResult.add(new CheckResult(node.getIp(),"机器："+node.getIp()+"manEng服务端口:"+basic.getMgPort()+"被占用"));
                    }
                }
                if(node.getTg()==1){
                    request.setCommand(AgentRestUtil.COMMAND_CHECK_PORT_USED+basic.getTgPort());
                    AgentResult result = AgentRestUtil.postAgentCommand(request);
                    if(result.getData().getRetcode() == 0){
                        checkResult.add(new CheckResult(node.getIp(),"机器："+node.getIp()+"transEng服务端口:"+basic.getTgPort()+"被占用"));
                    }
                    request.setCommand(AgentRestUtil.COMMAND_CHECK_PORT_USED+basic.getTgInnerPort());
                    result = AgentRestUtil.postAgentCommand(request);
                    if(result.getData().getRetcode() == 0){
                        checkResult.add(new CheckResult(node.getIp(),"机器："+node.getIp()+"transEng内部端口:"+basic.getTgInnerPort()+"被占用"));
                    }
                }
                if(node.getCg()==1){
                    request.setCommand(AgentRestUtil.COMMAND_CHECK_PORT_USED+basic.getCgPort());
                    AgentResult result = AgentRestUtil.postAgentCommand(request);
                    if(result.getData().getRetcode() == 0){
                        checkResult.add(new CheckResult(node.getIp(),"机器："+node.getIp()+"calEng服务端口:"+basic.getCgPort()+"被占用"));
                    }
                    request.setCommand(AgentRestUtil.COMMAND_CHECK_PORT_USED+basic.getCgMysqlPort());
                    result = AgentRestUtil.postAgentCommand(request);
                    if(result.getData().getRetcode() == 0){
                        checkResult.add(new CheckResult(node.getIp(),"机器："+node.getIp()+"calEng MySQL端口:"+basic.getCgMysqlPort()+"被占用"));
                    }
                }
                if(node.getDg()==1){
                    request.setCommand(AgentRestUtil.COMMAND_CHECK_PORT_USED+basic.getDgPort());
                    AgentResult result = AgentRestUtil.postAgentCommand(request);
                    if(result.getData().getRetcode() == 0){
                        checkResult.add(new CheckResult(node.getIp(),"机器："+node.getIp()+"datEng服务端口:"+basic.getDgPort()+"被占用"));
                    }
                }
                //检查安装目录是否存在及权限
                request.setCommand(AgentRestUtil.COMMAND_CHECK_DIR_EXIST.replaceAll("dir","~/"+basic.getSoftPath()));
                AgentResult result = AgentRestUtil.postAgentCommand(request);
                if(result.getData().getRetcode() != 0){
                    checkResult.add(new CheckResult(node.getIp(),"机器："+node.getIp()+"软件安装目录:"+basic.getSoftPath()+"不存在或无读写执行权限"));
                }
                //检查安装目录是否为空
                request.setCommand(AgentRestUtil.COMMAND_CHECK_DIR_EMPTY.replaceAll("dir","~/"+basic.getSoftPath()));
                result = AgentRestUtil.postAgentCommand(request);
                if(result.getData().getRetcode() != 0){
                    checkResult.add(new CheckResult(node.getIp(),"机器："+node.getIp()+"软件安装目录:"+basic.getSoftPath()+"不是空目录"));
                }
                //验证数据目录权限
                if(basic.getDataMode()==0){
                    if(node.getDg()==1 || node.getTg()==1){
                        request.setCommand(AgentRestUtil.COMMAND_CHECK_DIR_EXIST.replaceAll("dir",basic.getDataPath()));
                        result = AgentRestUtil.postAgentCommand(request);
                        if(result.getData().getRetcode() != 0){
                            checkResult.add(new CheckResult(node.getIp(),"机器："+node.getIp()+"数据盘目录:"+basic.getDataPath()+"不存在或无读写执行权限"));
                        }
                    }
                }else{
                    if(node.getDg()==1){
                        basic.getDgData().stream().forEach(dgPath ->{
                            request.setCommand(AgentRestUtil.COMMAND_CHECK_DIR_EXIST.replaceAll("dir",dgPath));
                            AgentResult rst = AgentRestUtil.postAgentCommand(request);
                            if(rst.getData().getRetcode() != 0){
                                checkResult.add(new CheckResult(node.getIp(),"机器："+node.getIp()+"datEng数据盘目录:"+dgPath+"不存在或无读写执行权限"));
                            }

                            request.setCommand(AgentRestUtil.COMMAND_CHECK_DIR_EMPTY.replaceAll("dir",dgPath));
                            rst = AgentRestUtil.postAgentCommand(request);
                            if(rst.getData().getRetcode() != 0){
                                checkResult.add(new CheckResult(node.getIp(),"机器："+node.getIp()+"datEng数据盘目录:"+dgPath+"不是空目录"));
                            }
                        });
                    }
                    if(node.getTg()==1){
                        request.setCommand(AgentRestUtil.COMMAND_CHECK_DIR_EXIST.replaceAll("dir",basic.getTgData()));
                        result = AgentRestUtil.postAgentCommand(request);
                        if(result.getData().getRetcode() != 0){
                            checkResult.add(new CheckResult(node.getIp(),"机器："+node.getIp()+"transEng数据盘目录:"+basic.getTgData()+"不存在或无读写执行权限"));
                        }

                        request.setCommand(AgentRestUtil.COMMAND_CHECK_DIR_EMPTY.replaceAll("dir",basic.getTgData()));
                        result = AgentRestUtil.postAgentCommand(request);
                        if(result.getData().getRetcode() != 0){
                            checkResult.add(new CheckResult(node.getIp(),"机器："+node.getIp()+"transEng数据盘目录:"+basic.getTgData()+"不是空目录"));
                        }

                        request.setCommand(AgentRestUtil.COMMAND_CHECK_DIR_EXIST.replaceAll("dir",basic.getTgLog()));
                        result = AgentRestUtil.postAgentCommand(request);
                        if(result.getData().getRetcode() != 0){
                            checkResult.add(new CheckResult(node.getIp(),"机器："+node.getIp()+"transEng日志盘目录:"+basic.getTgLog()+"不存在或无读写执行权限"));
                        }
                    }
                }

                // 检查cgroup目录及权限
                if(basic.getEnableCgp()==1){
                    String cgroupDir = "/sys/fs/cgroup/cpu/"+basic.getCgroupDir();
                    request.setCommand(AgentRestUtil.COMMAND_CHECK_DIR_EXIST.replaceAll("dir",cgroupDir));
                    result = AgentRestUtil.postAgentCommand(request);
                    if(result.getData().getRetcode() != 0){
                        checkResult.add(new CheckResult(node.getIp(),"机器："+node.getIp()+"cgroup目录:"+cgroupDir+"不存在或无读写执行权限"));
                    }
                }
            });
        });
        //检查附加参数JSON格式
        Map extraMap = null;
        if(StringUtils.isNotEmpty(basic.getExtra())){
            try {
                extraMap = JSON.parseObject(basic.getExtra(),Map.class);
            } catch (Exception e) {
                checkResult.add(new CheckResult("附加信息："+basic.getExtra()+"不是有效的json格式"));
            }
        }
        Map map = new HashMap();
        map.put("check",checkResult);
        if(checkResult.size()==0){
            //添加大集群
            KwHugeClusterInfo hugeClusterInfo = new KwHugeClusterInfo();
            hugeClusterInfo.setHclusterId(basic.getHclusterId());
            hugeClusterInfo.setVersion(basic.getVersion());
            hugeClusterInfo.setAppname(basic.getSoftPath());
            hugeClusterInfo.setExtraInfo(basic.getExtra());
            hugeClusterInfo.setKwRootDir(basic.getSoftPath());
            Integer isMups = extraMap==null?1:(Integer) extraMap.get("isMups");
            if(isMups == null){//默认为非多租户版本
                hugeClusterInfo.setIsMups(1);
            }else{
                hugeClusterInfo.setIsMups(isMups);
            }
            hugeClusterInfo.setStatus(ClusterConstant.STATUS_CREATING);

            if(extraMap==null) extraMap = new HashMap();
            extraMap.put("dataMode",basic.getDataMode());
            if(basic.getDataMode()==0){
                extraMap.put("dataPath",basic.getDataPath());
            }else{
                extraMap.put("dgData",basic.getDgData());
                extraMap.put("tgData",basic.getTgData());
                extraMap.put("tgLog",basic.getTgLog());
            }
            if(basic.getEnableCgp()==1){
                extraMap.put("cgroupDir",basic.getCgroupDir());
            }
            hugeClusterInfo.setExtraInfo(JSONObject.toJSONString(extraMap));
            clusterService.insertKwHugeClusterInfo(hugeClusterInfo);
            //多租户版本innerId从0开始，非多租户从1开始
            int innerId = hugeClusterInfo.getIsMups();
            innerId = innerId==0?1:0;
            for(ClusterZone zone:zones){
                //添加子集群
                KwClusterInfo clsInfo = new KwClusterInfo();
                clsInfo.setInnerId(String.valueOf(innerId));
                clsInfo.setHclusterId(hugeClusterInfo.getHclusterId());
                clsInfo.setRole("master");
                subClsService.insertKwClusterInfo(clsInfo);

                //添加子集群节点
                Long subClusterId = clsInfo.getId();
                for(ZoneNode node : zone.getNodes()){
                    if(node.getMg()==1){
                        nodeService.insertKwClusterNodeInfo(
                                this.buildClusterNode(basic, subClusterId,node,"mg"));
                    }
                    if(node.getTg()==1){
                        nodeService.insertKwClusterNodeInfo(
                                this.buildClusterNode(basic, subClusterId,node,"tg"));
                    }
                    if(node.getCg()==1){
                        nodeService.insertKwClusterNodeInfo(
                                this.buildClusterNode(basic, subClusterId,node,"cg"));
                    }
                    if(node.getDg()==1){
                        nodeService.insertKwClusterNodeInfo(
                                this.buildClusterNode(basic, subClusterId,node,"dg"));
                    }
                }
                innerId++;
            }

            //启动创建大集群任务
            SysJob job = new SysJob();
            job.setJobId(hugeClusterInfo.getId());
            job.setJobName("addCluster");
            job.setJobGroup("sys");
            job.setInvokeTarget("clusterTask.addCluster("+hugeClusterInfo.getId()+"L)");
            job.setMisfirePolicy(ScheduleConstants.MISFIRE_IGNORE_MISFIRES);
            job.setConcurrent(ScheduleConstants.CONCURRENT_NO);
            job.setStatus(ScheduleConstants.Status.NORMAL.getValue());
            job.setCreateBy(getUsername());
            jobService.runOnce(job);
        }
        return success(map);
    }

    public KwClusterNodeInfo buildClusterNode(ClusterBasic basic,Long clusterId,ZoneNode node,String type){
        KwClusterNodeInfo clsNode = new KwClusterNodeInfo();
        clsNode.setHclusterId(basic.getHclusterId());
        clsNode.setClusterId(clusterId);
        clsNode.setNodeType(type);
        clsNode.setHostIp(node.getIp());
        if("mg".equals(type)){
            clsNode.setPort(basic.getMgPort());
        }
        if("tg".equals(type)){
            clsNode.setPort(basic.getTgPort());
            clsNode.setInnerPort(basic.getTgInnerPort());
        }
        if("cg".equals(type)){
            clsNode.setPort(basic.getCgPort());
            clsNode.setInnerPort(basic.getCgMysqlPort());
        }
        if("dg".equals(type)){
            clsNode.setPort(basic.getDgPort());
        }
        return clsNode;
    }

    /**
     * 修改基础信息——大集群列
     */
    @PreAuthorize("@ss.hasPermi('cluster:cluster:edit')")
    @Log(title = "基础信息——大集群列", businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult edit(@RequestBody KwHugeClusterInfo kwHugeClusterInfo)
    {
        return toAjax(clusterService.updateKwHugeClusterInfo(kwHugeClusterInfo));
    }

    /**
     * 删除基础信息——大集群列
     */
    @PreAuthorize("@ss.hasPermi('cluster:cluster:remove')")
    @Log(title = "基础信息——大集群列", businessType = BusinessType.DELETE)
	@DeleteMapping("/{ids}")
    public AjaxResult remove(@PathVariable Long[] ids)
    {
        return toAjax(clusterService.deleteKwHugeClusterInfoByIds(ids));
    }
}
